#pragma once

#include "neighborhood.h"
//#include "neighborhood2.h"
#include "imagematrix.h"
#include "coordinate.h"
//#include "eightlayerim.h"
#include <string>
#include <vector>

using namespace std;
class Neighborhood;
class ImageMatrix;

class LongInstance
{
protected:
	vector<Coordinate> coordinate;		//!< When used on an image, this array holds the locations in which this instance exists.
	vector<Neighborhood*> neighborhood;	//!< The array of neighborhood. 0 is for current time, 1 for t-1, etc.
	int label;							//!< The label for the instance. Is usually either 0 or 1 (black or white)
	int conflictNum;					//!< Tells the number of instances that conflict this instance.
	
	/** Stores the total hash value for the whole Long Instance, not individual neighborhoods. Unique for up to 8 phases.*/
	unsigned long totalHash;
	
	
public:
	/** Adds a new coordinate as a new place in the image, which this coordinate exists.*/
	void addCoordinate(int row, int col) {this->coordinate.push_back(Coordinate(row,col));}
	void addCoordinate(const Coordinate& c) {this->coordinate.push_back(c);}
	void incrementConflictNum(void) {this->conflictNum++;}
	virtual int getFeatureSize(void) const;	//!< Returns the total number of small elements (pixel states) in the whole IF part.
	int neighborhoodNum(void) const {return (int)this->neighborhood.size();}	//!< Returns the number of Neighborhoods in the IF part

	inline Coordinate getCoordinate (int index) const {return this->coordinate[index];}
	void setConflictNum (int val) {this->conflictNum = val;}
	int getConflictNum (void) const {return this->conflictNum;}
	int getRowOfCoordinate (int index) const {return this->coordinate[index].row;}
	int getColOfCoordinate (int index) const {return this->coordinate[index].col;}
	int getLabel (void) const {return this->label;}
	void setLabel (int val);
	void setCoordinate (int index, int row, int col) 
	{
		this->coordinate[index].row = row;
		this->coordinate[index].col = col;
	}

	/** Clears all the members of the object, like it was just constructed.*/
	void clear (void);

	/** Advances the instance in time (e.g. when the instance is for transition from phase 0 to phase 1,
	 * and now we are making it a transition from to phase 0 to phase 2). Adds a new neighborhood to the 
	 * existing instance (the neighborhood at the most recent phase we have passed) and also updates the
	 * label to the new label.
	 */
	virtual void addPhase (const Neighborhood& newNB, int newLabel);

	/** Advances the LongInstance in time by adding a new neighborhood from the given ImageMatrix. The 
	 *  new neighborhood will be build around the row and col.
	 */
	virtual void addPhase (const ImageMatrix& image, int row, int col, int newLabel);
	
	/** Returns the value of the given feature in the given neighborhood. */
	virtual int getFeature (int neighborhoodIndex, int featureIndex) const;

	/** Returns the value of the given feature, if all the features in all the neighbors are put linearly in line. */
	virtual int getFeature (int longFeatureIndex) const;
		
	unsigned long getTotalHash() const;

	void setFeature (int neighborhoodIndex, int featureIndex, int value);

	void setFeature (int longFeatureIndex, int value);

	/**
	 * returns the number of coordinates in the longinstance.
	 */
	int getOccuranceNum (void) const;
	int getCooNum (void) const {return this->getOccuranceNum();} //!< The same as above, for a more convenient function name

	LongInstance & operator= (const LongInstance&);	//!< Assignment operator
		
	virtual bool hasSameFeature (const LongInstance&) const;		//!< Returns true if the feature array of both objects have the same elements.

	virtual bool hasSameFeature (const vector<Neighborhood*>& nb) const;	//!< Returns true if this longinstance has the same features as the given neighborhood vector.
	
	/** Returns true if both objects have the same partialHash for their corresponding neighborhoods.
	 * i.e. if the neighborhoods are the same.
	 */
	bool hasSamePartialHash (LongInstance& other) const;
	
	void print(void) const;							//!< Prints the feature array to the screen
	
	void printDetail(void) const;						//!< Prints all the member data to the screen

	friend ostream& operator<< (ostream& stream, const LongInstance& li);

	/** Returns true if this object can be the next phase (i.e. next step) of the given instance.*/
	bool isNextPhase (LongInstance& prevPhase) const;

	/** Returns true if this object can be the previous phase (i.e. previous step) of the given instance.*/	 
	bool isPrevPhase (LongInstance& nextPhase) const;
	
	/** Updates the totalHash value. */
	void updateTotalHash (void);

	/** Creates the object from a provided totalHash. The base is CELLSTATES 
	 * and the length of the array is size*NEIGHBORHOODSIZE1
	 */
	void updateFromHash (unsigned long totalHash, int size, int label);

	/** updates the neighborhoods based on the input partialhash array.*/
	void updateFromPartialHashArray(unsigned long* hashValue, int hashValueSize, int label);

	/** adds the coordinates of the other long instance object to this object. other object is not changed.*/
	void addOthersCoordinates ( const LongInstance& other);

	//int getTopCooRow (void); //!< Returns the row of the highest coordination in the
	//int getBottomCooRow (void);
	//int getLeftCooCol (void);
	//int getRightCoocol (void);

	/**
	 * Returns the borders of the smallest rectangle that covers all the coordinates in the object.
	 * Parameters are all output parameters.
	 */
	void getCooBorders (int& top, int& bottom, int& left, int& right) const;

	/**
	 * Returnes the net information size (in bits) stored in an object.
	 * The returned value is the minimum number of bits that efficiently can give us all the information
	 * in an object.
	 */
	int getNetInfoSizeInBits(void) const;

	void clearCoordinates(void) {this->coordinate.clear();}

	/** returns the size of one neighborhood in the objects, i.e. the number of features in any of the neighborhoods.*/
	virtual int getNeighborhoodSize(void) const;

	LongInstance ();

	LongInstance (const LongInstance& other);
	
	/** Creates the object from a provided string and a label*/
	LongInstance (string feature, int label);
	
	/** Creates the object from a provided int array whose pointer is given, and a label. */
	LongInstance (int* feature, int featureSize, int label);

	/** Creates the object around the given coordinate from a provided string and a label*/
	LongInstance (string feature, int label, int row, int col);
	
	/** Creates the object around the given coordinate from a provided 
	 * int array whose pointer is given, and a label. 
	 */
	LongInstance (int* feature, int featureSize, int label, int row, int col);
	
	LongInstance (unsigned int totalHash, int size, int label);

	LongInstance (unsigned int totalHash, int size, int label, int occ, int conf);

	//LongInstance (const EightLayerIM& images, int row, int col, int size); //Warning: LongInstance has no idea about EightLayerIM

	virtual ~LongInstance();

	//friend void ConvertLI2BtoLI2(const LongInstance2B& LI2B, LongInstance& LI);
	vector<Coordinate>& getCoordinateVector();
};

